package org.smile.strate.api;

import org.smile.collection.ArrayUtils;
import org.smile.collection.CollectionUtils;
import org.smile.collection.MapUtils;
import org.smile.commons.SmileRunException;
import org.smile.http.HttpMethod;
import org.smile.io.IOUtils;
import org.smile.io.ResourceUtils;
import org.smile.strate.action.Action;
import org.smile.strate.action.ActionContext;
import org.smile.strate.action.ActionElement;
import org.smile.strate.action.ClassElement;
import org.smile.strate.form.ActionParamBeanClass;
import org.smile.template.StringTemplate;
import org.smile.template.Template;
import org.smile.util.StringUtils;

import javax.servlet.ServletContext;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 封装service类的方法 生成的ajax js文件 内容
 * 
 * @author 胡真山
 *
 */
public class ApiService {
	/** 服务名称 与前端js 一致 */
	protected String serviceName;

	protected ServletContext context;

	/***
	 * 缓存下service生成的js代码
	 */
	private Map<String, ApiCode> serviceJsMap = new ConcurrentHashMap<String, ApiCode>();

	private Template codeTemplate;

	public ApiService(ServletContext context, String serviceName) throws IOException {
		this.serviceName = serviceName;
		this.context = context;
		InputStream is = ResourceUtils
				.loadFromClassPath(ApiServiceConstants.resourcePath + ApiServiceConstants.apiCodeTemplate);
		if (is == null) {
			throw new IOException("file not find " + ApiServiceConstants.apiCodeTemplate);
		}
		this.codeTemplate = new StringTemplate(Template.SMILE, IOUtils.readString(is));
	}

	public String getServiceName() {
		return serviceName;
	}

	public void setServiceName(String serviceName) {
		this.serviceName = serviceName;
	}

	/***
	 * 获取代码
	 * 
	 * @param path
	 * @param actionPath
	 * @param actionSuffix
	 * @return
	 * @throws Exception
	 */
	public ApiCode getApiCode(String path, String actionPath, String actionSuffix) throws Exception {
		ApiCode js = serviceJsMap.get(path);
		if (js == null) {
			js = initApiCode(path, actionPath, actionSuffix);
			serviceJsMap.put(path, js);
		}
		return js;
	}

	/**
	 * 转换http提交方式
	 * 
	 * @param methods
	 * @return
	 */
	protected String convertMethod(String[] methods) {
		String method = HttpMethod.POST.name().toLowerCase();
		if (ArrayUtils.notEmpty(methods)) {
			Set<String> set = CollectionUtils.hashSet(methods);
			if (!set.contains(HttpMethod.POST.name())) {
				if (set.contains(HttpMethod.GET.name())) {
					method = HttpMethod.GET.name().toLowerCase();
				} else {
					method = methods[0].toLowerCase();
				}
			}
		}
		return method;
	}

	/**
	 * 构造 service对象提供给 前端js类的方法代码
	 * 
	 * @param actionPath
	 * @param contextPath
	 * @return
	 * @throws Exception
	 */
	protected ApiCode initApiCode(String contextPath, String actionPath, String actionSuffix) throws Exception {

		Action action = ActionContext.getDefaultExecutor().getActionBean(serviceName, context);

		if (action == null) {
			throw new SmileRunException("no service named " + serviceName + " find ");
		}

		ClassElement classElement = ActionContext.getAllActionClazz().get(action.getClass().getName());

		Collection<ActionElement> actionElements = classElement.getActionElements();

		List<Map> methodList = new LinkedList();

		actionElements.forEach(ae -> {
			String method = convertMethod(ae.getHttpMethod());
			ActionParamBeanClass paramBeanClass = ae.getParamBeanClass();
			Map m = MapUtils.hashMap();
			m.put("type", method);
			m.put("name", ae.getName());
			m.put("note", ae.getNote());
			if (paramBeanClass != null) {
				m.put("args", paramBeanClass.getFieldNames());
				m.put("params", paramBeanClass.getProperties());
			}
			methodList.add(m);
		});

		// 初始化方法
		String name = StringUtils.getFirstCharUpper(serviceName);

		Map codeMap = MapUtils.hashMap();
		codeMap.put("name", name);
		codeMap.put("serviceName", serviceName);
		codeMap.put("contextPath", contextPath);
		codeMap.put("actionPath", actionPath);
		codeMap.put("methods", methodList);
		codeMap.put("note", classElement.getNote());
		codeMap.put("suffix", actionSuffix);
		String scriptCode = codeTemplate.processToString(codeMap);
		return new ApiCode(scriptCode);
	}

	protected static class ApiCode {
		long times;
		/** 代码内容 */
		String content;

		ApiCode(String content) {
			this.times = System.currentTimeMillis();
			this.content = content;
		}
	}
}
